import {
  createRxDatabase,
  type RxCollection,
  type RxDatabase,
  type RxDocument,
} from "rxdb";
import { getRxStorageDexie } from "rxdb/plugins/storage-dexie";
import {
  workspaceSchema,
  type WorkspaceDocType,
} from "../models/workspace.model";
import {
  collectionSchema,
  type CollectionDocType,
} from "../models/collection.model";

import {
  type ActiveSideBarTabDocType,
  activeSideBarTabSchema,
} from "../models/active-sidebar-tab.model";
import { tabSchema, type TabDocType } from "../models/tab.model";
import { addRxPlugin } from "rxdb";
import { RxDBMigrationPlugin } from "rxdb/plugins/migration";
import { RxDBUpdatePlugin } from "rxdb/plugins/update";
import { RxDBQueryBuilderPlugin } from "rxdb/plugins/query-builder";
import constants from "@app/constants/constants";
import { teamSchema, type TeamDocType } from "../models/team.model";
import {
  githubRepoSchema,
  type GithubRepoDocType,
} from "../models/github-repo.model";
import {
  environmentSchema,
  type EnvironmentDocType,
} from "../models/environment.model";
import {
  windowSettingsSchema,
  type WindowSettingsDocType,
} from "../models/window-settings-model";

import { releaseSchema, type ReleaseDocType } from "../models/release.model";
import { guideSchema, type GuideDocType } from "../models/guide.model";
// import { RxDBDevModePlugin } from "rxdb/plugins/dev-mode";
// addRxPlugin(RxDBDevModePlugin);
import {
  featureSwitchSchema,
  type FeatureSwitchDocType,
} from "../models/feature-switch.model";
import {
  guestUserSchema,
  type GuestUserDocType,
} from "../models/guest-user.model";
import { updatesSchema, type UpdatesDocType } from "../models/updates.model";
import {
  TestflowSchema,
  type TFRxContainerType,
} from "../models/testflow.model";
import { CollectionAuthTypeBaseEnum } from "@sparrow/common/types/workspace/collection-base";
import { CollectionNavigationTabEnum } from "@sparrow/common/types/workspace/collection-tab";
// import { RxDBDevModePlugin } from "rxdb/plugins/dev-mode";
// addRxPlugin(RxDBDevModePlugin);
import {
  defaultAutoGeneratedHeaders,
  defaultState,
} from "../../../../packages/@sparrow-common/src/utils/testFlow.helper";
import {
  recentWorkspaceSchema,
  type RecentWorkspaceDocType,
} from "src/models/recent-workspace.model";
import {
  aiRequestConversationsSchema,
  type AiRequestConversationsDocType,
} from "src/models/ai-request-conversations.model";
import { planSchema, type PlanDocType } from "../models/plan.model";

addRxPlugin(RxDBQueryBuilderPlugin);
addRxPlugin(RxDBMigrationPlugin);
addRxPlugin(RxDBUpdatePlugin);
export type WorkspaceDocument = RxDocument<WorkspaceDocType>;
export type WorkspaceContainer = RxCollection<WorkspaceDocType>;
export type CollectionContainer = RxCollection<CollectionDocType>;
export type PlanContainer = RxCollection<PlanDocType>;
export type CollectionDocument = RxDocument<CollectionDocType>;
export type TeamDocument = RxDocument<TeamDocType>;
export type TeamContainer = RxCollection<TeamDocType>;
export type EnvironmentContainer = RxCollection<EnvironmentDocType>;
export type EnvironmentDocument = RxDocument<EnvironmentDocType>;
export type GithubDocument = RxDocument<GithubRepoDocType>;
export type ReleaseDocument = RxDocument<ReleaseDocType>;
export type GuideDocumnet = RxDocument<GuideDocType>;
export type FeatureDocument = RxDocument<FeatureSwitchDocType>;
export type GuestDocument = RxDocument<GuestUserDocType>;
export type UpdatesDocument = RxDocument<UpdatesDocType>;
export type RecentWorkspaceDocument = RxDocument<RecentWorkspaceDocType>;
export type RecentWorkspaceContainer = RxCollection<RecentWorkspaceDocType>;
export type AiRequestConversationsDocument =
  RxDocument<AiRequestConversationsDocType>;
export type PlanDocument = RxDocument<PlanDocType>;
export type PlanCollection = RxCollection<PlanDocType>;
// collate all the Rx collections

export type TabDocument = RxDocument<TabDocType>;
export type TabContainer = RxCollection<TabDocType>;
export type ActiveSideBarTabDocument = RxDocument<ActiveSideBarTabDocType>;
export type ActiveSideBarTabContainer = RxCollection<ActiveSideBarTabDocType>;
export type WindowSettingsContainer = RxCollection<WindowSettingsDocType>;

// collate all the Rx collections
export type DatabaseCollections = {
  workspace: WorkspaceContainer;
  tab: TabContainer;
  collection: CollectionContainer;
  environment: EnvironmentContainer;
  activesidebartab: ActiveSideBarTabContainer;
  windowSettings: WindowSettingsContainer;
  team: TeamContainer;
  testflow: TFRxContainerType;
  recentworkspace: RecentWorkspaceContainer;
  plan: PlanContainer;
};

// define the Rx database type
export type DatabaseType = RxDatabase<DatabaseCollections>;

//RxDB Class
export class RxDB {
  private static instance: RxDB | null = null;
  public rxdb: DatabaseType | null = null;
  private constructor() {}

  public static getInstance(): RxDB {
    if (!RxDB.instance?.rxdb) {
      RxDB.instance = new RxDB();
    }
    return RxDB.instance;
  }

  public async getDb() {
    if (this.rxdb) return { rxdb: this.rxdb };
    // create the Rx database
    this.rxdb = await createRxDatabase<DatabaseCollections>({
      name: constants.RXDB_DB_NAME,
      storage: getRxStorageDexie(),
      ignoreDuplicate: true,
    });

    // add all collections
    await this.rxdb.addCollections({
      workspace: {
        schema: workspaceSchema,
        migrationStrategies: {
          1: function (oldDoc: WorkspaceDocument) {
            return oldDoc;
          },
          2: function (oldDoc: WorkspaceDocument) {
            return oldDoc;
          },
        },
      },
      tab: {
        schema: tabSchema,
        migrationStrategies: {
          1: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          2: function (oldDoc: TabDocument) {
            if (oldDoc) {
              oldDoc.persistence = "permanent";
            }
            return oldDoc;
          },
          3: function (oldDoc: TabDocument) {
            if (oldDoc?.property?.collection) {
              oldDoc.property.collection.auth = {
                bearerToken: "",
                basicAuth: {
                  username: "",
                  password: "",
                },
                apiKey: {
                  authKey: "",
                  authValue: "",
                  addTo: "Header",
                },
              };
              oldDoc.property.collection.state = {
                collectionAuthNavigation: CollectionAuthTypeBaseEnum.NO_AUTH,
                collectionNavigation: CollectionNavigationTabEnum.OVERVIEW,
              };
            }
            return oldDoc;
          },
          4: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          5: async function (oldDoc: TabDocument) {
            if (oldDoc?.property?.testflow) {
              const nodes = oldDoc.property.testflow.nodes ?? [];
              const workspaceId = oldDoc?.path?.workspaceId;

              if (nodes.length > 0) {
                const updatedNodes = [];

                for (const item of nodes) {
                  let updatedItem = { ...item };

                  if (item?.id === "1") {
                    const data = {
                      blockName: "startBlock",
                      collectionId: "",
                      folderId: "",
                      isDeleted: false,
                      requestData: null,
                      requestId: "",
                      workspaceId: "",
                    };
                    updatedItem = { ...item, data };
                  } else {
                    const { folderId, collectionId, requestId, name } =
                      item?.data;
                    let request;

                    // Reusable fetch logic
                    const collection = await RxDB.getInstance()
                      .rxdb.collection.findOne({
                        selector: { id: collectionId },
                      })
                      .exec();

                    if (collection) {
                      const items = collection.toJSON().items;

                      for (const element of items) {
                        if (folderId?.length > 0 && element.id === folderId) {
                          request = element.items.find(
                            (i) => i.id === requestId,
                          );
                        } else if (!folderId && element.id === requestId) {
                          request = element;
                        }

                        if (request) break;
                      }
                    }

                    if (request?.request) {
                      request.request.state = defaultState;
                      request.request.name = name;
                      request.request.autoGeneratedHeaders =
                        defaultAutoGeneratedHeaders;
                    }

                    const data = {
                      blockName: `block ${item?.id - 1}`,
                      collectionId,
                      folderId,
                      isDeleted: false,
                      requestData: request?.request,
                      requestId,
                      workspaceId,
                    };

                    updatedItem = {
                      ...item,
                      data,
                    };
                  }

                  updatedNodes.push(updatedItem);
                }

                // Update testflow nodes
                oldDoc.property.testflow.nodes = updatedNodes;
              }
            }

            return oldDoc;
          },
          6: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          7: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          8: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          9: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          10: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          11: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          12: function (oldDoc: TabDocument) {
            if (oldDoc?.property?.aiRequest) {
              oldDoc.property.aiRequest.state.isChatbotConversationLoading =
                false;
              oldDoc.property.aiRequest.state.isConversationHistoryPanelOpen =
                false;
              oldDoc.property.aiRequest.state.isConversationHistoryLoading =
                false;
            }
            return oldDoc;
          },
          13: function (oldDoc: TabDocument) {
            if (oldDoc?.property?.aiRequest) {
              oldDoc.property.aiRequest.state.isChatbotPromptBoxActive = true;
            }
            return oldDoc;
          },
          14: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          15: function (oldDoc: TabDocument) {
            if (oldDoc?.property?.request) {
              oldDoc.property.request.state.selectedRequestAuthProfileId = "";
            }
            if (oldDoc?.property?.aiRequest) {
              oldDoc.property.aiRequest.state.selectedRequestAuthProfileId = "";
            }
            return oldDoc;
          },
          16: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          17: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          18: function (oldDoc: TabDocument) {
            const test = {
              testCaseMode: "no-code",
              noCode: [],
              script: "",
            };
            if (oldDoc?.property?.request) {
              oldDoc.property.request.tests = test;
            }
            return oldDoc;
          },
          19: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          20: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          21: function (oldDoc: TabDocument) {
            if (oldDoc?.property?.testflow) {
              oldDoc.property.testflow.state = {
                testflowNavigator: "Testflow",
              };
            }
            return oldDoc;
          },
          22: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          23: function (oldDoc: TabDocument) {
            return oldDoc;
          },
          24: function (oldDoc: TabDocument) {
            const test = {
              testCaseMode: "no-code",
              noCode: [],
              script: "",
            };
            if (oldDoc?.property?.testflow?.nodes) {
              oldDoc.property.testflow.nodes =
                oldDoc.property.testflow.nodes.map((node) => {
                  if (node?.data?.requestData) {
                    node.data.requestData.tests = test;
                  }
                  return node;
                });
            }

            return oldDoc;
          },
        },
      },
      collection: {
        schema: collectionSchema,
        migrationStrategies: {
          1: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
          2: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
          3: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
          4: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
          5: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
          6: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
          7: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
          8: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
          9: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
          10: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
          11: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
          12: function (oldDoc: CollectionDocument) {
            return oldDoc;
          },
        },
      },
      activesidebartab: {
        schema: activeSideBarTabSchema,
      },
      windowsettings: {
        schema: windowSettingsSchema,
      },
      team: {
        schema: teamSchema,
        migrationStrategies: {
          1: function (oldDoc: TeamDocument) {
            return oldDoc;
          },
          2: function (oldDoc: TeamDocument) {
            return oldDoc;
          },
          3: function (oldDoc: TeamDocument) {
            return oldDoc;
          },
          4: function (oldDoc: TeamDocument) {
            return oldDoc;
          },
          5: function (oldDoc: TeamDocument) {
            return oldDoc;
          },
          6: function (oldDoc: TeamDocument) {
            return oldDoc;
          },
          7: function (oldDoc: TeamDocument) {
            return oldDoc;
          },
        },
      },
      environment: {
        schema: environmentSchema,
      },
      testflow: {
        schema: TestflowSchema,
        migrationStrategies: {
          1: async function (oldDoc) {
            if (oldDoc && oldDoc?.nodes?.length > 0) {
              const nodes = oldDoc?.nodes ?? [];
              const workspaceId = oldDoc?.workspaceId;

              if (nodes.length > 0) {
                const updatedNodes = [];

                for (const item of nodes) {
                  let updatedItem = { ...item };

                  if (item?.id === "1") {
                    const data = {
                      blockName: "startBlock",
                      collectionId: "",
                      folderId: "",
                      isDeleted: false,
                      requestData: null,
                      requestId: "",
                      workspaceId: "",
                    };
                    updatedItem = { ...item, data };
                  } else {
                    const { folderId, collectionId, requestId, name } =
                      item?.data;
                    let request;

                    // Reusable fetch logic
                    const collection = await RxDB.getInstance()
                      .rxdb.collection.findOne({
                        selector: { id: collectionId },
                      })
                      .exec();

                    if (collection) {
                      const items = collection.toJSON().items;

                      for (const element of items) {
                        if (folderId?.length > 0 && element.id === folderId) {
                          request = element.items.find(
                            (i) => i.id === requestId,
                          );
                        } else if (!folderId && element.id === requestId) {
                          request = element;
                        }

                        if (request) break;
                      }
                    }

                    if (request?.request) {
                      request.request.state = defaultState;
                      request.request.name = name;
                      request.request.autoGeneratedHeaders =
                        defaultAutoGeneratedHeaders;
                    }

                    const data = {
                      blockName: `block ${item?.id - 1}`,
                      collectionId,
                      folderId,
                      isDeleted: false,
                      requestData: request?.request,
                      requestId,
                      workspaceId,
                    };

                    updatedItem = {
                      ...item,
                      data,
                    };
                  }

                  updatedNodes.push(updatedItem);
                }

                // Update testflow nodes
                oldDoc.nodes = updatedNodes;
              }
            }
            return oldDoc;
          },
          2: function (oldDoc) {
            if (oldDoc && Array.isArray(oldDoc.nodes)) {
              const test = {
                testCaseMode: "no-code",
                noCode: [],
                script: "",
              };
              oldDoc.nodes = oldDoc.nodes.map((node) => {
                if (node?.data?.requestData) {
                  node.data.requestData.tests = test;
                }
                return node;
              });
            }
            return oldDoc;
          },
        },
      },
      plan: {
        schema: planSchema,
        migrationStrategies: {
          //   // database  migration functions
          1: function (oldDoc: PlanDocument) {
            return oldDoc;
          },
          2: function (oldDoc: PlanDocument) {
            return oldDoc;
          },
          3: function (oldDoc: PlanDocument) {
            return oldDoc;
          },
          4: function (oldDoc: PlanDocument) {
            return oldDoc;
          },
          5: function (oldDoc: PlanDocument) {
            return oldDoc;
          },
        },
      },
      githubrepo: {
        schema: githubRepoSchema,
      },
      releaseupdate: {
        schema: releaseSchema,
      },
      guide: {
        schema: guideSchema,
      },
      featureswitch: {
        schema: featureSwitchSchema,
      },
      guestuser: {
        schema: guestUserSchema,
      },
      updates: {
        schema: updatesSchema,
      },
      recentworkspace: {
        schema: recentWorkspaceSchema,
      },
      aiRequestConversations: {
        schema: aiRequestConversationsSchema,
      },
    });
    return { rxdb: this.rxdb };
  }

  /**
   * Destroys the RXDB Instance from the client
   */
  public async destroyDb(): Promise<void> {
    await this.rxdb?.destroy();
    await this.rxdb?.remove();
    this.rxdb = null;
    return;
  }
}
